/************************************
*
*  Taylor Boren
*  Assignment 4: Random Tester
*  Card: Smithy
*
*************************************/


#include "dominion.h"  
#include "dominion_helpers.h"  
#include "rngs.h"  
#include <stdio.h> 
#include <string.h>   
#include <math.h>
#include <time.h>  
#include <stdlib.h>  
#include <assert.h>

#define MAX_HAND 500
#define MAX_DECK 500

#define MAX_PLAYERS 4

int randomtest(struct gameState *post){
	struct gameState pre;
	int numPlayers, player, handCount, deckCount, discardCount, handPos, cardsDrawn, play, preTotal, postTotal, originalTotal, errors;

	errors = 0; // if this is not 0 after random tests, there was a bug/error somewhere (marked by an arrow)
	numPlayers = (int) rand() % 3 + 2; // makes ure there are 2-4 players
	player = rand() % numPlayers;
	handCount = rand() % MAX_HAND+1;
	deckCount = rand() % MAX_DECK;
	discardCount = rand() % MAX_DECK;
	pre.playedCardCount = post->playedCardCount = rand() % MAX_DECK;
	// set variables in gameState
	pre.whoseTurn = post->whoseTurn = player;
	pre.handCount[player] = post->handCount[player] = handCount;
	pre.deckCount[player] = post->deckCount[player] = deckCount;
	pre.discardCount[player] = post->discardCount[player] = discardCount;

	// the total number of cards should never change
	originalTotal = discardCount+deckCount+handCount;

	// copy game state with randomly assigned variables
	memcpy(&pre, post, sizeof(struct gameState));

	// since handPos is an input for smithy, we need to assign a random number and test it
	handPos = rand() % handCount;

	// play card
	play = cardEffect(smithy, 0, 0, 0, post, handPos, 0);
	postTotal = post->handCount[player] + post->deckCount[player] + post->discardCount[player];
	
	// create oracle for Smithy Card (use pre to simulate what smithy should do)
	pre.handCount[player]--; // play smithy card
	pre.discardCount[player]++; // add smithy to discard
	for(cardsDrawn = 0 ; cardsDrawn < 3 ; cardsDrawn++){
		drawCard(player, &pre);
	}

	// test that total number of cards didn't change
	preTotal = pre.deckCount[player]+pre.handCount[player]+pre.discardCount[player];
	printf("Original Total Cards = %i, \nPre (my implementation) Total Cards = %i, \nPost (dominion.c implementation) Total Cards = %i\n", originalTotal, preTotal, postTotal);
	if(originalTotal == postTotal && postTotal == preTotal && originalTotal == preTotal)
		printf("Total Cards Test: PASSES\n");
	else{
		errors++;
		printf("Total Card Test: FAILS <----------------------------SEE\n"); // arrow just makes errors more notiecable in output
	}

	// test that hand count is the same in pre and post
	printf("Pre (my implementation) Hand Count = %i, \nPost (dominion.c implementation) Hand Count = %i\n", pre.handCount[player], post->handCount[player]);
	if(pre.handCount[player] == post->handCount[player])
		printf("Hand Count Test: PASSES\n");
	else{
		errors++;
		printf("Hand Count Test: FAILS <----------------------------SEE\n");
	}

	// test that deck count is the same in pre and post
	printf("Pre (my implementation) Deck Count = %i, \nPost (dominion.c implementation) Deck Count = %i\n", pre.deckCount[player], post->deckCount[player]);
	if(pre.deckCount[player] == post->deckCount[player])
		printf("Deck Count Test: PASSES\n");
	else{
		errors++;
		printf("Deck Count Test: FAILS <----------------------------SEE\n");
	}

	// test that discard count is the same in pre and post
	printf("Pre (my implementation) Discard Count = %i, \nPost (dominion.c implementation) Discard Count = %i\n", pre.discardCount[player], post->discardCount[player]);
	if(pre.discardCount[player] == post->discardCount[player])
		printf("Discard Count Test: PASSES\n");
	else{
		errors++;
		printf("Discard Count Test: FAILS <----------------------------SEE\n");
	}
	// make sure we can get all the way through Smithy Effect
	assert(play == 0);
	// pre and post should be the same 
	// (commented out because this fails and exits, the print statements are more helpful in determining the bug)
	//assert(memcpy(&pre, post, sizeof(struct gameState)) == 0);
	printf("\n\n");
	if(errors != 0)
		return 1; // this allows me to see if every test fails or just tests with certain inputs
	else
		return 0;
}


int main(int argc, char *argv[])
{
	int seed, errors;
	struct gameState g;
	if(argv[1] != NULL)
		seed = (int)argv[1];
	else 
		seed = 123;
	srand(seed);
	errors = 0;
	int numTests = 10000;
	int i;
	printf("Starting Random Test For Smithy Card ...\n");
	for (i = 0; i < sizeof(struct gameState); i++) {
      ((char*)&g)[i] = floor(rand() * 256);
    }

	for(i = 0 ; i < numTests ; i++){
		errors += randomtest(&g);
	}
	if(errors == numTests){
		printf("ALL RANDOM TESTS FAILED:\n--->This means the bug occurs regardless of the inputs provided. \n\n");
		printf("***In this case, 2 cards 'disappear' (one from the deck and one from discard) every time smithy is played.\n\n\n");
	}
	else{
		printf("ONLY SOME RANDOM TESTS FAILED (%i):\n--->This means the bug occurs only with certain inputs.  See output above to locate inputs that cause failure in the test cases. \n\n\n\n", errors);
	}
	return 0;
}